#!/usr/bin/python3
# -*- coding: UTF-8 -*-
"""
文件说明：RST_UART_HAL.py
串口收发服务的抽象层。封装收发数据过程。
------------------------------------------
时间            作者        版本号      记录
2021-7-6        李树益      1.0         完成收发服务
"""
from .CRC import *
from .UART_RX import UART_RX
import serial       #导入串口模块
import threading    #导入多线程模块
import queue

"""
功能介绍：串口的硬件抽象层，封装串口收发过程
参数[port]：串口设备
"""
class UART_HAL:

    """
    功能介绍：打开串口服务，开启发送和接收线程
    参数[port]：串口设备
    返回：无
    """
    def __init__(self,port = '/dev/ttyUSB0'):   #传入参数port是串口端口号
        self.decode = UART_RX()
        self.tx_fifo = queue.Queue(5)    #数据发送队列
        self.tx_bytes = 0   #发送计数
        self.tx_packs = 0   #发送的数据包
        self.rx_bytes = 0   #接收计数
        self.rx_packs = 0   #接收到的数据包
        self.__send_thread = threading.Thread(target=self.__SendLoop)    #创建发送线程
        self.__recv_thread = threading.Thread(target=self.__RecvLoop)    #创建接收线程
        self.ser = serial.Serial(port,115200)             #打开指定串口，波特率115200

        if self.ser.isOpen() == True:      #判断串口是否打开
            self.__send_thread.start() #串口成功打开，启动发送线程
            self.__recv_thread.start() #接收线程

    """
    功能介绍：循环发送数据。在self.__send_thread中调用
    参数：无
    返回：无
    """
    def __SendLoop(self):
        print("串口发送线程已启动")
        while True:
            try:    #尝试从FIFO中获取一帧数据
                data = self.tx_fifo.get(timeout=1)  #设置1秒的超时（用于结束线程）
                #print("fifo ok")
            except: #获取失败
                if not self.ser.isOpen():
                    break   #串口已关闭，退出
                continue    #串口没关闭，继续，进入下一轮
            #print(len(data))
            #print(data)
            try:    #尝试发送数据帧
                self.tx_bytes += self.ser.write(data)    #串口发送数据
                #self.tx_bytes += len(data) 
                self.tx_packs += 1
                #print("Send {} Bytes".format(tx))
            except: #发送失败
                #print('tx error')
                if not self.ser.isOpen():
                    break   #串口已关闭，退出
        print("串口发送线程已结束")

    """
    功能介绍：按照通信协议接收数据。在self.__recv_thread中调用
    参数：无
    返回：无
    """
    def __RecvLoop(self):
        print("串口接收线程已启动")
        temp = b''
        while True:
            try:    #尝试从串口中读取一个字节
                temp = self.ser.read(1)
                self.rx_bytes += 1
                #print("Fa = {}".format(temp))
            except: #超时了或者其它情况，没读到
                if not self.ser.isOpen():
                    break   #串口已关闭，退出
                continue    #串口没关闭，继续，进入下一轮
            if temp != b'\xfa': #如果这次接收到的数据不是0xfa（帧起始标志），则重新开始
                continue
            try:    #尝试把帧头读取出来
                temp += self.ser.read(6)
                self.rx_bytes += 6
            except: #没读完，超时了
                continue
            #帧头校验
            ret,temp = self.__GetFrameHeader(temp)
            #print("CRC8 ",ret)
            if not ret: #如果帧头校验失败，重新来过
                continue
            datalenght = temp[5]    #获取有效负载长度
            try:    #尝试把有效数据和整包校验读出来
                temp += self.ser.read(datalenght+2)
                self.rx_bytes += datalenght+2
            except: #超时了，有问题
                continue
            #整包校验
            ret = self.__FrameChuck(temp,datalenght+9)
            #print("CRC16 ",ret)
            if not ret: #校验失败，进行下一轮
                continue
            self.decode.Decode(temp)    #数据解码并保存到数据集里边
            self.rx_packs += 1
        print("串口接收线程已结束")

    """
    功能介绍：寻找帧头
    参数[pdata]：接收到的帧头
    返回[ret]：找到帧头为True，找不到为False
    返回[pdata]：帧头
    """
    def __GetFrameHeader(self,pdata):
        ret = True
        if pdata[6:7] != CRC8(pdata,6):       #拿到的不是帧头
            offset = pdata[1:].find(0xfa)   #排除第一个字节之后，看看还有没有0xfa
            if(offset < 0):                 #如果没有0xfa，退出
                return False,b''
            pdata = pdata[offset+1:]        #如果有0xfa，看一下这个0xfa是不是帧的起始标志
            try:                            #尝试从串口中读取剩余的帧头
                pdata += self.ser.read(offset+1)
                self.rx_bytes += offset+1
            except: #超时了或者其它情况，没读到，退出
                return False,b''
            ret,pdata = self.__GetFrameHeader(pdata)  #读到数据，进行校验
        return ret,pdata

    """
    功能介绍：整包校验
    参数[pdata]：接收到的数据
    返回[ret]：校验成功为True，失败为False
    """
    def __FrameChuck(self,pdata,lenght):
        crc = pdata[lenght-2:]
        if crc != CRC16(pdata,lenght-2):
            return False
        else:
            return True
